package gcs

import (
	"fmt"
	"os/exec"
	"strings"

	"gitee.com/liumou_site/gbm"
	"github.com/spf13/cast"
)

// argsConfig 配置API的参数。
// 该方法主要用于初始化和配置ApiSudo对象的参数属性，为后续的API请求做准备。
func (api *ApiSudo) argsConfig() {
	// 初始化输出
	api.Strings = ""
	// 初始化执行结果
	api.Result = false
	// 初始化错误
	api.Err = nil
	// 重置执行结果
	api.Result = false
	// 如果启用了调试模式，记录处理前的参数数量
	if api.Debug {
		logs.Debug("处理前参数: %d", len(api.args))
	}
	// 去除空元素，确保参数列表中没有多余的空项
	api.args = gbm.SliceToSlice(api.args, " ")
	// 如果启用了调试模式，记录处理后的参数数量
	if api.Debug {
		logs.Debug("处理后参数: %d", len(api.args))
	}
	// 在切片头部添加-S参数，以满足特定的API要求或配置
	api.args = append([]string{"-S"}, api.args...)
}

// RunSudo 执行超级用户命令。
//
// 该方法允许ApiSudo实例执行需要超级用户权限的命令。它接受一个或多个字符串参数，
// 这些参数代表了需要执行的命令及其参数。通过将命令及其参数传递给api.shell()方法，
// 来执行实际的命令处理逻辑。
//
// 参数:
//
//	command ...string: 可变长度的字符串参数，代表需要执行的命令及其参数。
func (api *ApiSudo) RunSudo(command ...string) {
	// 将命令参数分配给api的args属性，为后续的命令执行做准备。
	api.args = command
	// 调用shell方法，利用api.args中存储的命令参数执行实际的命令。
	api.shell()
}

// RunSudoList 执行一系列sudo命令
// 该方法遍历命令数组，使用ApiSudo的RunSudo方法逐个执行命令
// 如果遇到执行失败的命令，它会记录错误并打印该命令，然后终止进一步执行
func (api *ApiSudo) RunSudoList(cs []string) {
	// 遍历命令数组
	for i, c := range cs {
		// 执行单个sudo命令
		api.RunSudo(c)
		// 检查执行命令后是否有错误
		if api.Err != nil {
			// 记录哪条命令执行失败
			logs.Error("第[ %d ]条命令执行失败", i)
			// 打印失败的命令
			fmt.Println(c)
			// 终止进一步执行并返回
			return
		}
	}
}

// RunScriptSudo 执行脚本命令。
//
// RunScriptSudo 方法允许以提升的权限执行给定的命令。它首先将命令参数转换为脚本，
// 如果转换过程中发生错误，则直接通过 shell 执行该命令。
// 此方法主要用于需要以管理员权限运行命令的场景。
func (api *ApiSudo) RunScriptSudo(command ...string) {
	// 设置 ScriptMode 为 true，表示当前操作处于脚本模式。
	api.ScriptMode = true

	// 将传入的命令参数赋值给 api 的 args 属性。
	api.args = command

	// 将命令参数转换为字符串，以便进一步处理。
	api.Text = gbm.SliceToString(command, " ")

	// 尝试根据转换后的命令创建脚本文件。
	err := createScript(api.Text, api.Script, false)

	// 如果创建脚本过程中出现错误，则将命令作为脚本文件名，并通过 shell 执行。
	if err != nil {
		api.args = []string{api.Script}
		api.shell()
	}

	// 重置 ScriptMode 为 false，结束脚本模式。
	api.ScriptMode = false
}

// shell 执行shell命令，根据是否是root权限或使用sudo进行不同处理
func (api *ApiSudo) shell() {
	// 参数解析与初始化
	api.argsConfig()

	// 检查是否以root权限运行
	if api.isRoot {
		// 当使用root权限运行时，根据调试模式记录日志
		if api.Debug {
			logs.Debug("Currently running with root permission(当前使用root权限运行)")
		}
		// 调用系统shell执行命令
		api.shellSystem()

		// 成功执行后，设置结果状态
		if api.Err == nil {
			api.Result = true
		}
		return
	} else {
		// 当不以root权限运行时，输出调试信息并使用sudo提权
		if api.Debug {
			logs.Debug("Using sudo authorization(正在使用sudo提权)")
			fmt.Println(api.args)
		}
	}

	// 根据是否需要实时输出，选择不同的执行方式
	if api.Realtime {
		api.sudoBase()
	} else {
		api.getout()
	}
}

// getout 执行外部命令，并获取输出结果。
// 该方法主要用于执行需要sudo权限的命令，并根据需要提供密码。
func (api *ApiSudo) getout() {
	// 定义要使用sudo执行的命令
	cmd := exec.Command("sudo", api.args...)

	// 如果当前用户不是root，则将密码作为命令的标准输入
	if !api.isRoot {
		cmd.Stdin = strings.NewReader(api.Password)
	}

	// 捕获命令的输出
	output, err := cmd.CombinedOutput()

	// 将命令的输出结果和错误信息存储到api对象中
	api.Strings = string(output)
	api.Err = err

	// 如果有错误，记录错误日志
	if err != nil {
		logs.Error(api.Err.Error())
	}
}

// sudoBase 是 ApiSudo 结构体的一个方法，用于执行需要sudo权限的命令或脚本。
// 它处理了命令的执行细节，包括设置sudo命令、处理输入输出、错误处理等。
func (api *ApiSudo) sudoBase() {
	cmd := exec.Command("sudo", api.args...)              // 创建命令实例
	api.Text = "sudo " + gbm.SliceToString(api.args, " ") // 记录实际运行内容
	if api.ScriptMode {
		if api.Debug {
			logs.Debug("正在运行脚本: ", api.Script)
		}
		cmd = exec.Command("sudo", "-S", api.Script)
	}
	if !api.isRoot {
		cmd.Stdin = strings.NewReader(api.Password) // 如果不是Root用户、将密码作为命令的标准输入
	}
	stdout, err := cmd.StdoutPipe() // 创建管道获取输出

	if err != nil {
		if api.PrintErr {
			logs.Error("管道创建失败,SudoPath: ", api.SudoPath)
		}
		if api.Debug {
			logs.Debug("Execute command: ", api.Text)
		}
		api.ExitCode = 1
		api.Strings = "管道创建失败"
		api.Err = err
		return
	}
	cmd.Stderr = cmd.Stdout // 命令的错误输出和标准输出都连接到同一个管道

	api.Err = cmd.Start() // 开始运行命令
	if api.Err != nil {
		if api.PrintErr {
			logs.Error("命令启动失败 :", api.Err)
		}
		if api.Debug {
			logs.Debug("Execute command: ", api.Text)
		}
		api.ExitCode = 2
		api.Strings = "命令启动失败"
		return
	}

	for {
		// 从管道中实时获取输出并打印到终端
		tmp := make([]byte, 1024)
		_, err := stdout.Read(tmp)
		fmt.Print(string(tmp)) // 如果开启了实时打印,则将信息逐行输出到终端
		api.Strings = api.Strings + cast.ToString(string(tmp))
		if err != nil {
			break
		}
	}

	api.Err = cmd.Wait()                       // 等待命令退出，并等待任何复制到stdin或从stdout或stderr复制完成
	api.ExitCode = cmd.ProcessState.ExitCode() // ExitCode返回已退出进程的退出代码，如果进程尚未退出或被信号终止，则返回-1
	if api.PrintInfo {
		logs.Info("Exit Code: ", api.ExitCode)
	}
	if api.Err == nil {
		if api.ExitCode != 0 {
			if api.PrintErr {
				logs.Error("Error :", api.Err)
			}
			if api.Debug {
				logs.Debug("Execute command: ", api.Text)
			}
			return
		}
		if api.ExitCode == 0 {
			api.Err = nil
			api.Result = true
			return
		}
	} else {
		if api.PrintErr {
			logs.Error("Error :", api.Err)
		}
		if api.Debug {
			logs.Debug("Execute command: ", api.Text)
		}
	}
}

// SudoTimeOut 支持设置超时的命令执行函数
//func (shells *ApiShell) SudoTimeOut(cmd string, timeout int) (string, error) {
//	ctx, cancel := context.WithTimeout(context.Background(), time.Duration(timeout)*time.Second)
//	defer cancel()
//	args, ok := Shell.Split(cmd) // 对命令进行切割
//	// 如果切割失败则返回
//	if !ok {
//		mess := fmt.Sprintf("unbalanced quotes or backslashes in [%s]", cmd)
//		return mess, fmt.Errorf("命令切割失败,请检查是否使用了特殊符号")
//	}
//	run := []string{"-S"}
//	run = append(run, args...)
//	cmds := exec.CommandContext(ctx, "sudo", run...)
//	return shells.Execute()
//}
